﻿using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.JavaScript;
using System.Runtime.Serialization;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
using System.Xml;
using Mediapipe.Net.Framework.Format;
using Mediapipe.Net.Framework.Protobuf;
using Mediapipe.Net.Solutions;
using OpenCvSharp;
using Object = System.Object;
using Window = System.Windows.Window;
using Point = OpenCvSharp.Point;
using Size = OpenCvSharp.Size;
using Rect = OpenCvSharp.Rect;
using System.Diagnostics;
using System.Windows.Media.Animation;

namespace FaceProject;

/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
    private Config SetConfing = new();

    //摄像头编号，使用默认摄像头为0,
    private int captureIndex = 1;

    // 捕获当前的摄像头
    private VideoCapture? myCapture;

    //是否显示图片
    private static bool _isShowImage = true;

    // 关键点
    private static readonly int keyPoints = 468;

    //人脸边界数组
    private static int[] newFaceBorderIndexArr =
    {
        151, 108, 69, 104, 68, 21, 162, 127, 234, 93, 132, 58, 172, 136, 150, 149, 176, 148, 152, 377, 400, 378, 379,
        365, 397, 288, 361, 323, 454, 356, 389, 251, 298, 333, 299, 337
    };

    private static int[,] triangulationPointsIndex =
    {
        { 337, 132, 323 },
        { 337, 151, 132 },
        { 151, 132, 93 },
        { 151, 93, 234 },
        { 151, 234, 108 },
        { 108, 234, 127 },
        { 108, 127, 162 },
        { 108, 69, 162 },
        { 69, 162, 21 },
        { 69, 21, 104 },
        { 104, 21, 68 },
        { 132, 323, 361 },
        { 132, 361, 58 },
        { 58, 361, 288 },
        { 58, 288, 397 },
        { 58, 397, 365 },
        { 58, 365, 379 },
        { 58, 379, 378 },
        { 58, 378, 150 },
        { 150, 378, 400 },
        { 150, 400, 377 },
        { 150, 377, 152 },
        { 152, 148, 176 },
        { 150, 152, 176 },
        { 150, 149, 176 },
        { 58, 150, 136 },
        { 150, 149, 176 },
        { 337, 323, 454 },
        { 337, 299, 454 },
        { 299, 298, 454 },
        { 299, 333, 298 },
        { 298, 389, 251 },
        { 298, 389, 356 },
        { 298, 454, 356 }
    };

    private static Mat dstImage = new();
    private static Mat srcImage = new();
    private static Mat OutputImage; //待改

    // 摄像头的宽度
    private static int captureWidth = 1280;

    // 摄像头的高度
    private static int captureHeight = 720;

    private static string[,] srcPath =
    {
        { @"Assets\Face\mask\1.1.png", @"Assets\Face\mask\1.2.png" },
        { @"Assets\Face\mask\2.1..png", @"Assets\Face\mask\2.2.png" },
        { @"Assets\Face\mask\3.1.png", @"Assets\Face\mask\3.2.png" },
        { @"Assets\Face\mask\4.1.png", @"Assets\Face\mask\4.2.png" },
        { @"Assets\Face\mask\5.1.png", @"Assets\Face\mask\5.2.png" },
        { @"Assets\Face\mask\6.1.png", @"Assets\Face\mask\6.2.png" },
        { @"Assets\Face\mask\7.1.png", @"Assets\Face\mask\7.2.png" },
        { @"Assets\Face\mask\8.1.png", @"Assets\Face\mask\8.2.png" },
        { @"Assets\Face\mask\9.1.png", @"Assets\Face\mask\9.2.png" }
    };

    private Mat templateMask;
    private Mat myFaceMask;
    private Mat tempImage;

    // private Point[] srcFaceBorderPoints = new Point[newFaceBorderIndexArr.Length];
    private static Point[,] FaceBorderPoints = new Point[srcPath.GetLength(0), newFaceBorderIndexArr.Length];
    private static Point[] dstFaceBorderPoints = new Point[newFaceBorderIndexArr.Length];
    private static Point[] currentSrcFacePoint = new Point[FaceBorderPoints.GetLength(1)];

    //倒计时
    private DispatcherTimer disTimer = new();

    private FaceMeshCpuSolution faceMeshCpuSolution = new();

    //初始倒计时秒数
    private int countSecond = 5;

    private Mat OverlayFace;
    private Mat srcFace;

    //源图像三角形点
    private Point srcFacePoint0;
    private Point srcFacePoint1;

    private Point srcFacePoint2;

    //目标图像三角形点
    private Point dstFacePoint0;
    private Point dstFacePoint1;

    private Point dstFacePoint2;

    //仿射三角形掩码
    private Mat triangleMask;

    //仿射变换矩阵或者仿射掩码公用变量
    private Mat affineMatrixOrMask;

    //接受仿射结果
    private Mat transformedTriangle = new();

    //接收处理完成的结果
    private Mat res;

    //接受截取的三角形脸部内容
    private Mat srcTriangle;

    //液化半径
    private static double radius = 150;

    //瘦脸程度
    private static float _strength;

    //瘦脸程度k值
    private static float _k = 19f;

    //瘦脸的人脸边界点索引
    private static readonly int[] slimmingFaceIndexArr =
    {
        147, 376
    };

    //瘦脸的人脸边界点坐标
    private static Point[] slimmingFacePoint = new Point[slimmingFaceIndexArr.Length];

    //人脸中心点
    private Point faceCenter;

    private bool isSwap = true;

    private int FristCreamIndex = 1;

    private static double BackHomeMin = 5;
    private DispatcherTimer _idleTimer;
    private TimeSpan _idleTimeSpan;
    private readonly TimeSpan _maxIdleTime = TimeSpan.FromSeconds(BackHomeMin * 60);

    /// <summary>
    /// 是否打印关键点
    /// </summary>
    private bool isShowPoint = true;

    private double FaceHeight;
    private double FaceWidth;

    private int MaxWidth = 220;
    private int MinWidth = 100;

    private bool HaveFace = false;
    public MainWindow()
    {
        InitializeComponent();
        this.Loaded += OnLoaded; //卡顿问题

        _idleTimer = new DispatcherTimer();
        _idleTimer.Interval = TimeSpan.FromSeconds(1);
        _idleTimer.Tick += OnIdleTimerTick;
        _idleTimeSpan = TimeSpan.Zero;

        // 启动定时器
        _idleTimer.Start();
    }

    // 定时器 Tick 事件处理
    private void OnIdleTimerTick(object sender, EventArgs e)
    {
        _idleTimeSpan += _idleTimer.Interval;
        if (_idleTimeSpan >= _maxIdleTime)
        {
            // 倒计时结束，返回首页
            NavigateToHomePage();
            ResetIdleTimer();
        }
    }

    // 重置定时器
    private void ResetIdleTimer()
    {
        _idleTimeSpan = TimeSpan.Zero;
    }

    private void NavigateToHomePage()
    {
        HomeBackgroundVideo.Play();
        Home.IsSelected = true;
        OrganPage.IsSelected = false;
        FaceChange.IsSelected = false;
        SwapResult1.IsSelected = false;
        SwapResult2.IsSelected = false;
        StopOrganVideo();
        if (myCapture != null)
        {
            _isShowImage = false;
            myCapture.Dispose();
            myCapture = null;
        }

        FindTip.Text = "请将面部对准识别框";
        countSecond = 5;
        disTimer = new DispatcherTimer();
        Lung.IsSelected = false;
        Liver.IsSelected = false;
        Kidney.IsSelected = false;
        Brain.IsSelected = false;
        Heart.IsSelected = false;
        Stomach.IsSelected = false;
    }

    private void OnLoaded(object sender, RoutedEventArgs e)
    {
        ReadInfoFromXml();
        FindSrcFacePoint();
        HomeBackgroundVideo.Source = new Uri(@"Assets\Home\HomeVideo.mp4",
            UriKind.RelativeOrAbsolute);
        FirstCreamBackground.Source =
            new BitmapImage(new Uri(@"Assets\Home\BackgroundImage.png", UriKind.RelativeOrAbsolute));
        ThirdCreamBackground.Source =
            new BitmapImage(new Uri(@"Assets\Home\BackgroundImage.png", UriKind.RelativeOrAbsolute));
        OrganBackgroundVideo.Source =
            new Uri(@"Assets\Organ\人体视频.mp4",
                UriKind.RelativeOrAbsolute);
        LiverVideo.Source = new Uri(@"Assets\Organ\Liver\video.mp4",
            UriKind.RelativeOrAbsolute);
        KidneyVideo.Source = new Uri(@"Assets\Organ\Kidney\video.mp4",
            UriKind.RelativeOrAbsolute);
        LungVideo.Source = new Uri(@"Assets\Organ\Lung\video.mp4",
            UriKind.RelativeOrAbsolute);
        BrainVideo.Source = new Uri(@"Assets\Organ\Brain\video.mp4",
            UriKind.RelativeOrAbsolute);
        HeartVideo.Source = new Uri(@"Assets\Organ\Heart\video.mp4",
            UriKind.RelativeOrAbsolute);
        StomachVideo.Source = new Uri(@"Assets\Organ\Stomach\video.mp4",
            UriKind.RelativeOrAbsolute);

        LungAudio.Source = new Uri(@"Assets\Organ\Lung\sound.mp3", UriKind.RelativeOrAbsolute);
        LiverAudio.Source = new Uri(@"Assets\Organ\Liver\sound.mp3", UriKind.RelativeOrAbsolute);
        KidneyAudio.Source = new Uri(@"Assets\Organ\Kidney\sound.mp3", UriKind.RelativeOrAbsolute);
        BrainAudio.Source = new Uri(@"Assets\Organ\Brain\sound.mp3", UriKind.RelativeOrAbsolute);
        HeartAudio.Source = new Uri(@"Assets\Organ\Heart\sound.mp3", UriKind.RelativeOrAbsolute);
        StomachAudio.Source = new Uri(@"Assets\Organ\Stomach\sound.mp3", UriKind.RelativeOrAbsolute);

        FaceAudio.Source = new Uri(@"Assets\Face\m2.mp3", UriKind.RelativeOrAbsolute);

        HomeBackgroundVideo.Play();
        MediaTimeline backgroundAudio =
            new MediaTimeline(new Uri(@"Assets/Home/bgMusic.mp3", UriKind.RelativeOrAbsolute));
        backgroundAudio.RepeatBehavior = RepeatBehavior.Forever;
        MediaClock mClock = backgroundAudio.CreateClock();
        BtnAudio.Clock = mClock;
    }

    private void Face_Btn_Click(Object s, RoutedEventArgs e)
    {
        ResetIdleTimer();
        HomeBackgroundVideo.Stop();
        FaceAudio.Stop();
        HomeBackgroundVideo.Position = TimeSpan.Zero;
        if (!_isShowImage)
        {
            _isShowImage = true;
        }

        Home.IsSelected = false;
        FaceChange.IsSelected = true;
        SetCaptur();
    }


    private void Organ_Btn_Click(Object s, RoutedEventArgs e)
    {
        ResetIdleTimer();
        OrganBackgroundVideo.Play();
        LiverVideo.Play();
        KidneyVideo.Play();
        LungVideo.Play();
        BrainVideo.Play();
        HeartVideo.Play();
        StomachVideo.Play();
        HomeBackgroundVideo.Stop();
        LungAudio.Stop();
        LiverAudio.Stop();
        KidneyAudio.Stop();
        BrainAudio.Stop();
        HeartAudio.Stop();
        StomachAudio.Stop();
        HomeBackgroundVideo.Position = TimeSpan.Zero;
        Home.IsSelected = false;
        OrganPage.IsSelected = true;
    }

    private void OrganBack_Btn_Click(Object s, RoutedEventArgs e)
    {
        ResetIdleTimer();
        OrganPage.IsSelected = false;
        StopOrganVideo();
        HomeBackgroundVideo.Play();
        Home.IsSelected = true;
        Lung.IsSelected = false;
        Liver.IsSelected = false;
        Kidney.IsSelected = false;
        Brain.IsSelected = false;
        Heart.IsSelected = false;
        Stomach.IsSelected = false;
        SwapResult1.IsSelected = false;
        SwapResult2.IsSelected = false;
    }

    private void FaceBack_Btn_Click(Object s, RoutedEventArgs e)
    {
        ResetIdleTimer();
        if (myCapture != null)
        {
            _isShowImage = false;
            myCapture.Dispose();
            myCapture = null;
        }

        FindTip.Text = "请将面部对准识别框";
        countSecond = 5;
        disTimer.Stop();
        disTimer = new DispatcherTimer();
        HomeBackgroundVideo.Play();
        FaceChange.IsSelected = false;
        Home.IsSelected = true;
        SwapResult1.IsSelected = false;
        SwapResult2.IsSelected = false;
        _strength = 15;
    }

    private void Take_Btn_Click(Object s, RoutedEventArgs e)
    {
        ResetIdleTimer();
        if (!disTimer.IsEnabled && FaceChange.IsSelected && isSwap)
        {
            if (MinWidth > FaceWidth)
            {
                FindTip.Text = "请稍微站近一点！";
            }
            else if(FaceWidth > MaxWidth)
            {
                FindTip.Text = "请稍微站远一点！";
            }
            else
            {
                CountDown();
            }
        }
    }

    private void MediaElement_MediaEnded(Object sender, RoutedEventArgs e)
    {
        HomeBackgroundVideo.Position = TimeSpan.Zero;
    }

    private void MediaElement_MediaOpened(Object sender, RoutedEventArgs e)
    {
        OrganBackgroundVideo.Play(); // 重新播放视频
    }

    private void OrganBackground_MediaEnded(Object sender, RoutedEventArgs e)
    {
        OrganBackgroundVideo.Position = TimeSpan.Zero;
    }

    private void OrganBackground_MediaOpened(Object sender, RoutedEventArgs e)
    {
        HomeBackgroundVideo.Play(); // 重新播放视频
    }

    private void PlayOrganVideo(Object sender, RoutedEventArgs e)
    {
        ResetIdleTimer();
        LiverVideo.Play();
        KidneyVideo.Play();
        LungVideo.Play();
        BrainVideo.Play();
        HeartVideo.Play();
        StomachVideo.Play();
    }

    private void StopOrganVideo()
    {
        ResetIdleTimer();
        LiverVideo.Stop();
        KidneyVideo.Stop();
        LungVideo.Stop();
        BrainVideo.Stop();
        HeartVideo.Stop();
        StomachVideo.Stop();
        OrganBackgroundVideo.Stop();
    }

    private void ResetOrganVideoPosition(Object sender, RoutedEventArgs e)
    {
        LiverVideo.Position = TimeSpan.Zero;
        KidneyVideo.Position = TimeSpan.Zero;
        LungVideo.Position = TimeSpan.Zero;
        BrainVideo.Position = TimeSpan.Zero;
        HeartVideo.Position = TimeSpan.Zero;
        StomachVideo.Position = TimeSpan.Zero;
        OrganBackgroundVideo.Position = TimeSpan.Zero;
    }

    private void Lung_Btn_Click(Object s, RoutedEventArgs e)
    {
        ResetIdleTimer();
        LungAudio.Play();
        LiverAudio.Stop();
        StomachAudio.Stop();
        BrainAudio.Stop();
        HeartAudio.Stop();
        KidneyAudio.Stop();
        Lung.IsSelected = true;
        Liver.IsSelected = false;
        Kidney.IsSelected = false;
        SwapResult1.IsSelected = false;
    }

    private void Liver_Btn_Click(Object s, RoutedEventArgs e)
    {
        ResetIdleTimer();
        LungAudio.Stop();
        LiverAudio.Play();
        StomachAudio.Stop();
        BrainAudio.Stop();
        HeartAudio.Stop();
        KidneyAudio.Stop();
        Lung.IsSelected = false;
        Liver.IsSelected = true;
        Kidney.IsSelected = false;
        SwapResult1.IsSelected = false;
    }

    private void Kidney_Btn_Click(Object s, RoutedEventArgs e)
    {
        ResetIdleTimer();
        LungAudio.Stop();
        LiverAudio.Stop();
        StomachAudio.Stop();
        BrainAudio.Stop();
        HeartAudio.Stop();
        KidneyAudio.Play();
        Lung.IsSelected = false;
        Liver.IsSelected = false;
        Kidney.IsSelected = true;
        SwapResult1.IsSelected = false;
    }

    private void Brain_Btn_Click(Object s, RoutedEventArgs e)
    {
        ResetIdleTimer();
        LungAudio.Stop();
        LiverAudio.Stop();
        StomachAudio.Stop();
        BrainAudio.Play();
        HeartAudio.Stop();
        KidneyAudio.Stop();
        Brain.IsSelected = true;
        Heart.IsSelected = false;
        Stomach.IsSelected = false;
        SwapResult2.IsSelected = false;
    }

    private void Heart_Btn_Click(Object s, RoutedEventArgs e)
    {
        ResetIdleTimer();
        LungAudio.Stop();
        LiverAudio.Stop();
        StomachAudio.Stop();
        BrainAudio.Stop();
        HeartAudio.Play();
        KidneyAudio.Stop();
        Brain.IsSelected = false;
        Heart.IsSelected = true;
        Stomach.IsSelected = false;
        SwapResult2.IsSelected = false;
    }

    private void Stomach_Btn_Click(Object s, RoutedEventArgs e)
    {
        ResetIdleTimer();
        LungAudio.Stop();
        LiverAudio.Stop();
        StomachAudio.Play();
        BrainAudio.Stop();
        HeartAudio.Stop();
        KidneyAudio.Stop();
        Brain.IsSelected = false;
        Heart.IsSelected = false;
        Stomach.IsSelected = true;
        SwapResult2.IsSelected = false;
    }

    /// <summary>
    /// 配置摄像头
    /// </summary>
    private void SetCaptur()
    {
        if (myCapture == null)
        {
            myCapture = new VideoCapture(captureIndex, VideoCaptureAPIs.DSHOW);
            //设置视频捕获器高度
            myCapture.Set(VideoCaptureProperties.FrameWidth, captureWidth);
            myCapture.Set(VideoCaptureProperties.FrameHeight, captureHeight);
            if (!myCapture.IsOpened()) MessageBox.Show("摄像头开启失败");

            ThreadPool.QueueUserWorkItem(_ => StartCapture());
        }
        else
        {
            MessageBox.Show("相机已打开");
        }
    }

    /// <summary>
    /// 释放摄像头
    /// </summary>
    private Point findFaceCenter;
    
    /// <summary>
    /// 开始换脸入口函数
    /// </summary>
    private void StartCapture()
    {
        while (_isShowImage) // 当 _isShowImage 为真时，持续捕捉图像
        {
            using (Mat temp = new Mat())
            {
                try
                {
                    if (countSecond == -1)
                    {
                        // 调用异步方法播放音频
                        Application.Current.Dispatcher.InvokeAsync(() => FaceAudio.Play());
                    }

                    if (myCapture != null && myCapture.Read(temp) && !temp.Empty())
                    {
                        Cv2.Flip(temp, temp, FlipMode.Y);
                        
                        // 如果启用换脸功能
                        if (isSwap)
                        {
                            ProcessSwap(temp); // 处理换脸逻辑
                        }

                        // 更新 UI 中的图像显示
                        Application.Current.Dispatcher.Invoke(() => { Face.Source = matToBitmapImage(temp); });
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine($"StartCaptureFuncErrorMessage: {e.Message}");
                }
            }
        }
    }

    /// <summary>
    /// 处理捕获的视频帧进行人脸换脸处理
    /// </summary>
    /// <param name="temp">捕获的帧</param>
    private void ProcessSwap(Mat temp)
    {
        dstImage = temp.Clone(); // 克隆捕捉到的图像
        List<NormalizedLandmarkList>? tempList = FindFace(dstImage); // 查找人脸关键点
        if (tempList != null && tempList[0].Landmark.Any())
        {
            HaveFace = true;
            var dstItem = tempList[0].Landmark;
            findFaceCenter = new Point((int)Math.Ceiling(temp.Width * dstItem[5].X),
                    (int)Math.Ceiling(temp.Height * dstItem[5].Y));
            ComputedFaceSize(tempList, temp); // 计算人脸尺寸
            for (int i = 0; i < newFaceBorderIndexArr.Length; i++)
            {
                var tempPoint = dstItem[newFaceBorderIndexArr[i]];
                Point dstRelativePoint = new Point((int)Math.Ceiling(temp.Width * tempPoint.X),
                    (int)Math.Ceiling(temp.Height * tempPoint.Y));
                dstFaceBorderPoints[i] = dstRelativePoint; // 保存相对坐标

                if (isShowPoint)
                {
                    // 显示关键点
                    if(FaceWidth > MinWidth && FaceWidth < MaxWidth)
                    {
                        Cv2.Circle(temp, dstRelativePoint, 2, Scalar.Aqua, -1);
                    }
                    else
                    {
                        Cv2.Circle(temp, dstRelativePoint, 2, Scalar.Red, -1);
                    }
                    Cv2.Circle(dstImage, dstRelativePoint, 2, Scalar.Aqua, -1);
                }

                if (i < slimmingFaceIndexArr.Length)
                {
                    var item = tempList[0].Landmark[slimmingFaceIndexArr[i]];
                    Point RelativePoint = new Point((int)Math.Ceiling(temp.Width * item.X),
                        (int)Math.Ceiling(temp.Height * item.Y));
                    slimmingFacePoint[i] = RelativePoint; // 保存瘦脸点
                }
            }
        }
        else
        {
            HaveFace = false;
            for (int i = 0; i < newFaceBorderIndexArr.Length; i++)
            {
                dstFaceBorderPoints[i] = new Point(0,0); // 保存相对坐标
            }
        }

        AddAlphaChannel(dstImage); // 添加 alpha 通道
    }

    /// <summary>
    /// 计算人脸胖瘦并设置瘦脸强度
    /// </summary>
    /// <param name="tempList">识别到的人脸百分比关键点</param>
    /// <param name="temp">人脸源Mat对象</param>
    private void ComputedFaceSize(List<NormalizedLandmarkList?> tempList, Mat temp)
    {
        var dstItem1 = tempList[0].Landmark[10];
        var dstItem2 = tempList[0].Landmark[152];
        var dstItem3 = tempList[0].Landmark[234];
        var dstItem4 = tempList[0].Landmark[454];


        Point dstRelativePoint1 = new Point((int)Math.Ceiling(temp.Width * dstItem1.X),
            (int)Math.Ceiling(temp.Height * dstItem1.Y));
        Point dstRelativePoint2 = new Point((int)Math.Ceiling(temp.Width * dstItem2.X),
            (int)Math.Ceiling(temp.Height * dstItem2.Y));
        Point dstRelativePoint3 = new Point((int)Math.Ceiling(temp.Width * dstItem3.X),
            (int)Math.Ceiling(temp.Height * dstItem3.Y));
        Point dstRelativePoint4 = new Point((int)Math.Ceiling(temp.Width * dstItem4.X),
            (int)Math.Ceiling(temp.Height * dstItem4.Y));

         FaceHeight =
            Math.Sqrt((dstRelativePoint2.X - dstRelativePoint1.X) * (dstRelativePoint2.X - dstRelativePoint1.X) +
                      (dstRelativePoint2.Y - dstRelativePoint1.Y) * (dstRelativePoint2.Y - dstRelativePoint1.Y));
        FaceWidth =
            Math.Sqrt((dstRelativePoint4.X - dstRelativePoint3.X) * (dstRelativePoint4.X - dstRelativePoint3.X) +
                      (dstRelativePoint4.Y - dstRelativePoint3.Y) * (dstRelativePoint4.Y - dstRelativePoint3.Y));
        double faceSize = FaceHeight / FaceWidth;
        _strength = (float)(_k * faceSize);
    }

    /// <summary>
    ///将 Mat类型转为BitmapImage类型
    /// </summary>
    /// <param name="mat">要转换的Mat对象</param>
    /// <returns>转换后的BitmapImage结果</returns>
    private BitmapImage matToBitmapImage(Mat mat)
    {
        using (Bitmap bitmap = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(mat))
        {
            BitmapImage bitmapImage = new BitmapImage();
            using (MemoryStream memory = new MemoryStream())
            {
                bitmap.Save(memory, System.Drawing.Imaging.ImageFormat.Bmp);
                memory.Position = 0;
                bitmapImage.BeginInit();
                bitmapImage.StreamSource = memory;
                bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
                bitmapImage.EndInit();
                bitmapImage.Freeze(); // 优化性能，使对象不可变
            }

            return bitmapImage;
        }
    }

    /// <summary>
    /// 识别人脸方法
    /// </summary>
    private void FindSrcFacePoint()
    {
        for (int i = 0; i < srcPath.GetLength(0); i++)
        {
            Mat src = new Mat(srcPath[i, 0]); // 读取源图像
            var srcFacePointPosition = FindFace(src); // 查找源图像中的人脸关键点

            if (srcFacePointPosition != null && srcFacePointPosition[0].Landmark.Count > 0)
            {
                for (int j = 0; j < newFaceBorderIndexArr.Length; j++)
                {
                    var srcItem = srcFacePointPosition[0].Landmark[newFaceBorderIndexArr[j]];
                    Point srcRelativePoint = new Point((int)Math.Ceiling(src.Width * srcItem.X),
                        (int)Math.Ceiling(src.Height * srcItem.Y));
                    FaceBorderPoints[i, j] = srcRelativePoint; // 保存源图像关键点的相对坐标
                }
            }
        }
    }

    /// <summary>
    /// 获取人脸分割三角形点的坐标
    /// </summary>
    /// <param name="pointIndex">点的索引</param>
    /// <returns></returns>
    public Point[] GetArrayRow(int pointIndex)
    {
        var res = new Point[FaceBorderPoints.GetLength(1)];
        for (int i = 0; i < FaceBorderPoints.GetLength(1); i++)
        {
            res[i] = FaceBorderPoints[pointIndex, i];
        }

        return res;
    }

    /// <summary>
    /// 开始换脸操作。
    /// </summary>
    private async void StarSwap()
    {
        disTimer.Stop();
        if (HaveFace)
        {
            Application.Current.Dispatcher.Invoke(new Action(() =>
            {
                ImageSource imageSource = matToBitmapImage(new Mat(@"Assets\Face\man00.png"));
                SwapResult1.IsSelected = true;
                SwapResult2.IsSelected = true;
                TwoYear.Source = imageSource;
                FourYear.Source = imageSource;
                SixYear.Source = imageSource;
                EightYear.Source = imageSource;
                TenYear.Source = imageSource;
                TwelfthYear.Source = imageSource;
                FourteenthYear.Source = imageSource;
                SixteenthYear.Source = imageSource;
                EighteenthYear.Source = imageSource;
            }));

            if (dstImage.Empty())
            {
                return; // 如果目标图像为空，则直接返回
            }

            for (int i = 0; i < srcPath.GetLength(0); i++)
            {
                srcImage = new Mat(srcPath[i, 0], ImreadModes.Unchanged);
                OutputImage = srcImage.Clone();

                // 初始化目标图掩码图层
                templateMask = new Mat(dstImage.Size(), MatType.CV_8UC1, Scalar.Black);

                var SwapRes = FaceSwap(i);

                _strength += 3;
                foreach (var item in slimmingFaceIndexArr)
                {
                    int index = Array.IndexOf(slimmingFaceIndexArr, item);
                    Point slimmingPoint = slimmingFacePoint[index];
                    SwapRes = LocalTranslationWarpFastWith_strength(SwapRes, slimmingPoint.X, slimmingPoint.Y, faceCenter.X,
                        faceCenter.Y);
                }

                await Task.Run(() => UpdateUI(i, SwapRes));
            }
        }
        Application.Current.Dispatcher.Invoke(new Action(() =>
        {
            FindTip.Text = "请将面部对准识别框";
            isSwap = true;
            countSecond = 5;
            disTimer = new DispatcherTimer();
            _strength = 15;
        }));
    }

    /// <summary>
    /// 放置对应的图像到对应的图像控件
    /// </summary>
    /// <param name="index">控件索引</param>
    /// <param name="image">要展示的图像</param>
    private void UpdateUI(int index, Mat image)
    {
        ImageSource imageSource = matToBitmapImage(image);
        Application.Current.Dispatcher.InvokeAsync(() =>
        {
            switch (index)
            {
                case 0:
                    TwoYear.Source = imageSource;
                    break;
                case 1:
                    FourYear.Source = imageSource;
                    break;
                case 2:
                    SixYear.Source = imageSource;
                    break;
                case 3:
                    EightYear.Source = imageSource;
                    break;
                case 4:
                    TenYear.Source = imageSource;
                    break;
                case 5:
                    TwelfthYear.Source = imageSource;
                    break;
                case 6:
                    FourteenthYear.Source = imageSource;
                    break;
                case 7:
                    SixteenthYear.Source = imageSource;
                    break;
                case 8:
                    EighteenthYear.Source = imageSource;
                    break;
                default:
                    Console.WriteLine("UpdateUI Index out of expected range");
                    break;
            }
        });
    }

    /// <summary>
    /// 识别人脸方法
    /// </summary>
    /// <param name="faceImage">含有人脸的图像</param>
    /// <returns>识别到的人脸的百分比关键点</returns>
    private List<NormalizedLandmarkList>? FindFace(Mat faceImage)
    {
        var faceImageData = new byte[faceImage.Width * faceImage.Height * faceImage.Channels()];
        Marshal.Copy(faceImage.Data, faceImageData, 0, faceImageData.Length);
        ImageFrame guoFace = new ImageFrame(ImageFormat.Types.Format.Srgb, faceImage.Width, faceImage.Height,
            faceImage.Width * faceImage.Channels(), faceImageData);
        var faceOutput = faceMeshCpuSolution.Compute(guoFace);
        return faceOutput;
    }

    /// <summary>
    /// 分割识别到的脸和源图像中的脸，进行映射
    /// </summary>
    /// <param name="srcFaceIndex">源图像索引</param>
    /// <returns>映射后的Mat对象，即换完脸后的Mat对象</returns>
    private Mat FaceSwap(int srcFaceIndex)
    {
        currentSrcFacePoint = GetArrayRow(srcFaceIndex);
        srcFace = new Mat();
        myFaceMask = new Mat(srcImage.Size(), MatType.CV_8UC1, Scalar.Black);
        if (currentSrcFacePoint.All(x => x != new Point(0, 0)))
        {
            //设置目标图掩码图层
            Cv2.FillPoly(templateMask, new[] { dstFaceBorderPoints }, Scalar.White);
            //裁取源图脸部
            Cv2.FillPoly(myFaceMask, new[] { currentSrcFacePoint }, Scalar.White);
            Cv2.BitwiseAnd(srcImage, srcImage, srcFace, myFaceMask);

            //脸部的分割，仿射和融合
            for (int i = 0; i < triangulationPointsIndex.GetLength(0); i++)
            {
                //定义当前三角形三点的索引
                int point0Index = Array.IndexOf(newFaceBorderIndexArr, triangulationPointsIndex[i, 0]);
                int point1Index = Array.IndexOf(newFaceBorderIndexArr, triangulationPointsIndex[i, 1]);
                int point2Index = Array.IndexOf(newFaceBorderIndexArr, triangulationPointsIndex[i, 2]);

                //拿取源图对应索引的三个点
                srcFacePoint0 = currentSrcFacePoint[point0Index];
                srcFacePoint1 = currentSrcFacePoint[point1Index];
                srcFacePoint2 = currentSrcFacePoint[point2Index];

                //填充三角形区域，创建三角形掩码
                triangleMask = new Mat(srcImage.Size(), MatType.CV_8UC1, Scalar.Black);
                //填充三角区域
                Cv2.FillConvexPoly(triangleMask, new[] { srcFacePoint0, srcFacePoint1, srcFacePoint2 },
                    Scalar.White);
                Cv2.Dilate(triangleMask, triangleMask, Cv2.GetStructuringElement(MorphShapes.Rect, new Size(3, 3)));

                //截取当前三角形内的人脸部分
                srcTriangle = new Mat();
                Cv2.BitwiseAnd(srcFace, srcFace, srcTriangle, triangleMask);

                //转储源图点为Point2f数组
                Point2f[] src =
                {
                    new(srcFacePoint0.X, srcFacePoint0.Y),
                    new(srcFacePoint1.X, srcFacePoint1.Y),
                    new(srcFacePoint2.X, srcFacePoint2.Y)
                };
                //拿取目标三角形三点
                dstFacePoint0 = dstFaceBorderPoints[point0Index];
                dstFacePoint1 = dstFaceBorderPoints[point1Index];
                dstFacePoint2 = dstFaceBorderPoints[point2Index];
                //转储目标图点为Point2f数组
                Point2f[] dst =
                {
                    new(dstFacePoint0.X, dstFacePoint0.Y),
                    new(dstFacePoint1.X, dstFacePoint1.Y),
                    new(dstFacePoint2.X, dstFacePoint2.Y)
                };

                // 计算仿射变换矩阵
                affineMatrixOrMask = Cv2.GetAffineTransform(src, dst);
                // 对源图像进行仿射变换
                Cv2.WarpAffine(srcTriangle, transformedTriangle, affineMatrixOrMask, dstImage.Size(),
                    InterpolationFlags.Cubic, BorderTypes.Reflect);
                //叠加图片
                affineMatrixOrMask = new Mat(dstImage.Size(), MatType.CV_8UC1, Scalar.Black);
                Cv2.FillConvexPoly(affineMatrixOrMask, new[] { dstFacePoint0, dstFacePoint1, dstFacePoint2 },
                    Scalar.White);
                transformedTriangle.CopyTo(OutputImage, affineMatrixOrMask);
            }

            dstImage.CopyTo(triangleMask);
            OverlayFace = MergeImagesWithMask(dstImage, OutputImage, templateMask);
            OverlayFace = DeleteAlphaChannel(OverlayFace);
            dstImage = DeleteAlphaChannel(dstImage);
            //计算包围点集的最小矩形边界框
            var r = Cv2.BoundingRect(dstFaceBorderPoints);
            //计算贴图的中心点
            faceCenter = new Point(r.Left + r.Width / 2, r.Top + r.Height / 2);
            //准备无缝融合后的空Mat对象
            res = new Mat(dstImage.Size(), srcImage.Type());

            //无缝融合
            Cv2.SeamlessClone(OverlayFace, dstImage, templateMask, faceCenter, res, SeamlessCloneMethods.NormalClone);
            return res;
        }

        return dstImage;
    }

    /// <summary>
    /// 开始拍照倒计时
    /// </summary>
    private void CountDown()
    {
        // 检查定时器是否已经运行
        if (!disTimer.IsEnabled)
        {
            disTimer.Tick += time_Tick;
            disTimer.Interval = TimeSpan.FromSeconds(1);
            disTimer.Start();
        }
    }

    /// <summary>
    /// 定时器的 Tick 事件处理方法，用于更新倒计时和触发换脸操作。
    /// </summary>
    /// <param name="sender">事件发送者，一般为定时器对象。</param>
    /// <param name="e">事件参数。</param>
    private void time_Tick(object? sender, EventArgs e)
    {
        if (countSecond == -1)
        {
            isSwap = false;
            disTimer.Stop();
            ThreadPool.QueueUserWorkItem(_ => { StarSwap(); });
        }
        else
        {
            if (HaveFace)
            {
                FindTip.Text = countSecond == 0 ? "请稍等..." : countSecond.ToString();
                countSecond--;
            }
            else
            {
                FindTip.Text = "请将脸对准识别框！";
                disTimer.Stop();
                disTimer = new DispatcherTimer();
                countSecond = 5;
            }
        }
    }

    /// <summary>
    /// 为3通道的Mat对象，增加Alpha通道，为4通道
    /// </summary>
    /// <param name="img">需要增加通道的Mat对象</param>
    /// <returns>返回处理完成后的4通道图像</returns>
    private Mat AddAlphaChannel(Mat img)
    {
        if (img.Channels() == 4)
        {
            return img;
        }

        Cv2.CvtColor(img, img, ColorConversionCodes.BGR2BGRA);
        return img;
    }

    /// <summary>
    /// 删除Alpha通道方法
    /// </summary>
    /// <param name="img">要处理的4通道Mat对象</param>
    /// <returns>处理之后的3通道Mat对象</returns>
    private Mat DeleteAlphaChannel(Mat img)
    {
        if (img.Channels() == 3)
        {
            return img;
        }

        Cv2.CvtColor(img, img, ColorConversionCodes.BGRA2BGR);
        return img;
    }

    /// <summary>
    /// 使用掩码将 PNG 图像合并到 JPG 图像上。
    /// </summary>
    /// <param name="jpgImg">JPG 图像的 Mat 对象。</param>
    /// <param name="pngImg">PNG 图像的 Mat 对象，必须包含 alpha 通道。</param>
    /// <param name="mask">掩码图像的 Mat 对象，仅在掩码值为 255 的位置进行合并。</param>
    /// <returns>合并后的图像的 Mat 对象。</returns>
    public Mat MergeImagesWithMask(Mat jpgImg, Mat pngImg, Mat mask)
    {
        // 如果 JPG 图像只有 3 个通道（即没有 alpha 通道），则添加 alpha 通道
        if (jpgImg.Channels() == 3)
        {
            jpgImg = AddAlphaChannel(jpgImg);
        }

        // 克隆 JPG 图像以创建结果图像
        Mat result = jpgImg.Clone();

        // 遍历图像的每个像素
        for (int y = 0; y < jpgImg.Rows; y++)
        {
            for (int x = 0; x < jpgImg.Cols; x++)
            {
                // 仅处理掩码值为 255 的像素
                if (mask.At<byte>(y, x) == 255)
                {
                    // 获取 PNG 图像中当前像素的值
                    Vec4b pngPixel = pngImg.At<Vec4b>(y, x);

                    // 获取 alpha 值并计算其逆值
                    double alpha = pngPixel[3] / 255.0;
                    double alphaInverse = 1 - alpha;
                    Vec4b resultPixel = result.At<Vec4b>(y, x);

                    // 混合 PNG 像素和结果像素的 RGB 值
                    for (int c = 0; c < 3; c++)
                    {
                        resultPixel[c] = (byte)(alpha * pngPixel[c] + alphaInverse * resultPixel[c]);
                    }

                    // 将结果像素的 alpha 通道设置为完全不透明
                    resultPixel[3] = 255;
                    result.Set(y, x, resultPixel);
                }
            }
        }

        // 返回合并后的结果图像
        return result;
    }

    /// <summary>
    /// 以下静态对象是下面瘦脸算法中需要的对象，轻易勿动！！！
    /// </summary>
    private static Mat copyImg;

    private static Mat maskImg;
    private static Size size;
    private static Mat mapX;
    private static Mat mapY;
    private static Mat distance_x;
    private static Mat distance_y;
    private static Mat distance;
    private static Mat K1;
    private static Mat tx;
    private static Mat ty;
    private static Mat scale_x;
    private static Mat scale_y;
    private static Mat UX;
    private static Mat UY;
    private static Mat mask;
    private static Mat resultImg;

    /// <summary>
    /// 瘦脸算法
    /// </summary>
    /// <param name="srcImg">包含人脸的Mat对象</param>
    /// <param name="startX">开始的X轴点</param>
    /// <param name="startY">结束的Y轴点</param>
    /// <param name="endX">结束的X点</param>
    /// <param name="endY">结束的Y轴点</param>
    /// <returns>瘦完脸后的结果</returns>
    private static Mat LocalTranslationWarpFastWith_strength(Mat srcImg, int startX, int startY, int endX, int endY)
    {
        //radius^2
        double ddradius = radius * radius;
        copyImg = new Mat();
        Cv2.CopyTo(srcImg, copyImg);

        maskImg = new Mat(srcImg.Size(), MatType.CV_8UC1, Scalar.All(0));
        Cv2.Circle(maskImg, startX, startY, (int)Math.Ceiling(radius), Scalar.All(255), -1);

        //k0 = 100 / strnegth
        double K0 = 100 / _strength;
        int dx = endX - startX;
        int dy = endY - startY;
        //(m(x)-c(x))^2
        double ddmc_x = Math.Pow(dx, 2);
        //(m(y)-c(y))^2
        double ddmc_y = Math.Pow(dy, 2);

        size = srcImg.Size();
        int H = size.Height;
        int W = size.Width;

        //圆内所有的点
        //x-c(x)
        mapX = new Mat(); //x轴
        //y-c(y)
        mapY = new Mat(); //y轴
        Cv2.Repeat(new Mat(1, W, MatType.CV_32FC1, Enumerable.Range(0, W).Select(i => (float)i).ToArray()), H, 1, mapX);
        Cv2.Repeat(new Mat(H, 1, MatType.CV_32FC1, Enumerable.Range(0, H).Select(i => (float)i).ToArray()), 1, W, mapY);


        //(x-c(x))^2
        distance_x = new Mat();
        //(y-c(y))^2
        distance_y = new Mat();
        Cv2.Subtract(mapX, startX, distance_x);
        Cv2.Subtract(mapY, startY, distance_y);
        Cv2.Pow(distance_x, 2, distance_x);
        Cv2.Pow(distance_y, 2, distance_y);

        //((x-c(x))^2 + (y-c(y))^2)
        distance = new Mat();
        Cv2.Add(distance_x, distance_y, distance);

        K1 = new Mat();
        //开根((x-c(x))^2 + (y-c(y))^2)
        Cv2.Sqrt(distance, K1);

        //(radius^2 - ((x-c(x))^2))/((radius^2 - (x-c(x))^2) + K0 * (m(x)-c(x))^2) = tx
        tx = new Mat();
        //(radius^2 - ((y-c(y))^2))/((radius^2 - (y-c(y))^2) + K0 * (m(y)-c(y))^2) = ty
        ty = new Mat();
        Cv2.Divide(ddradius - distance_x, (ddradius - distance_x) + K0 * ddmc_x, tx);
        Cv2.Divide(ddradius - distance_y, (ddradius - distance_y) + K0 * ddmc_y, ty);

        //(radius^2 - ((x-c(x))^2))/((radius^2 - (x-c(x))^2) + K0 * (m(x)-c(x))^2)^2
        Cv2.Pow(tx, 2, tx);
        //(radius^2 - ((y-c(y))^2))/((radius^2 - (y-c(y))^2) + K0 * (m(y)-c(y))^2)^2
        Cv2.Pow(ty, 2, ty);

        scale_x = 1.0 - K1 / radius;
        scale_y = 1.0 - K1 / radius;

        UX = new Mat();
        UY = new Mat();
        //(x-c(x)) - (((radius^2 - ((x-c(x))^2))/((radius^2 - (x-c(x))^2) + K0 * (m(x)-c(x))^2)^2)*(1 - k1/radius))
        Cv2.Multiply(tx, (endX - startX) * scale_x, UX);
        Cv2.Multiply(tx, (endY - startY) * scale_y, UY);
        Cv2.Subtract(mapX, UX, UX);
        Cv2.Subtract(mapY, UY, UY);

        mask = new Mat();
        Cv2.Compare(maskImg, Scalar.All(0), mask, CmpType.EQ);

        mapX.CopyTo(UX, mask);
        mapY.CopyTo(UY, mask);
        UX.ConvertTo(UX, MatType.CV_32FC1);
        UY.ConvertTo(UY, MatType.CV_32FC1);

        resultImg = new Mat();
        Cv2.Remap(srcImg, resultImg, UX, UY, InterpolationFlags.Linear, BorderTypes.Constant, Scalar.All(0));

        return resultImg;
    }

    /// <summary>
    /// 读取xml配置文件方法
    /// </summary>
    private void ReadInfoFromXml()
    {
        // 判断当前程序目录中是否存在该配置文件
        if (File.Exists("MZ.xml"))
        {
            using (FileStream FS = new FileStream("MZ.xml", FileMode.Open, FileAccess.Read))
            {
                // 创建序列化器
                DataContractSerializer Ser = new DataContractSerializer(typeof(Config));
                try
                {
                    SetConfing = Ser.ReadObject(FS) as Config;
                    this.Width = SetConfing.Width;
                    this.Height = SetConfing.Height;
                    ScaleTransform windowsScaleTransform = new ScaleTransform();
                    windowsScaleTransform.ScaleX = SetConfing.ScaleX;
                    windowsScaleTransform.ScaleY = SetConfing.ScaleY;
                    MainCanvas.RenderTransform = windowsScaleTransform;
                    _k = SetConfing.k;
                    radius = SetConfing.Radius;
                    countSecond = SetConfing.CountSecond;
                    captureIndex = SetConfing.CaptureIndex;
                    captureWidth = SetConfing.CaptureWidth;
                    captureHeight = SetConfing.CaptureHeight + 6;
                    this.Top = SetConfing.WindowTop - 6;
                    this.Left = SetConfing.WindowLeft;
                    FristCreamIndex = SetConfing.HomePageIndex;
                    isShowPoint = SetConfing.isShowPoint == 0 ? false:true ;
                    BackHomeMin = SetConfing.BackHomeTime;
                    MaxWidth = SetConfing.FaceMaxWidth;
                    MinWidth = SetConfing.FaceMinWidth;
                    if (FristCreamIndex == 1)
                    {
                        SecondCream.Margin = new Thickness(0, 0, 0, 0);
                        FirstCream.Margin = new Thickness(1080, 0, 0, 0);
                        ThirdCream.Margin = new Thickness(2160, 0, 0, 0);
                    }
                    else if (FristCreamIndex == 2)
                    {
                        SecondCream.Margin = new Thickness(1080, 0, 0, 0);
                        FirstCream.Margin = new Thickness(0, 0, 0, 0);
                        ThirdCream.Margin = new Thickness(2160, 0, 0, 0);
                    }
                    else if (FristCreamIndex == 3)
                    {
                        SecondCream.Margin = new Thickness(2160, 0, 0, 0);
                        FirstCream.Margin = new Thickness(0, 0, 0, 0);
                        ThirdCream.Margin = new Thickness(1080, 0, 0, 0);
                    }
                    else
                    {
                        MessageBox.Show("默认页位置设置错误！");
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine("ReadInfoFromXmlFuncErrorMessage:" + e.Message);
                }
            }
        }
        // 如果不存在就设置和创建配置文件
        else
        {
            SetConfing.Width = 3240;
            SetConfing.Height = 1920;
            SetConfing.ScaleX = 1;
            SetConfing.ScaleY = 1;
            SetConfing.CaptureIndex = 0;
            SetConfing.CaptureWidth = 1280;
            SetConfing.CaptureHeight = 720;
            SetConfing.CountSecond = 5;
            SetConfing.Radius = 150;
            SetConfing.k = 15;
            SetConfing.HomePageIndex = 2;
            SetConfing.WindowTop = 0;
            SetConfing.WindowLeft = 0;
            SetConfing.BackHomeTime = 5;
            SetConfing.isShowPoint = 0;
            SetConfing.FaceMinWidth = 100;
            SetConfing.FaceMaxWidth = 220;

            // 创建序列化器
            DataContractSerializer SetConfigData = new DataContractSerializer(typeof(Config));
            // 声明XMLWriterSettings对象
            XmlWriterSettings Settings = new XmlWriterSettings { Indent = true };
            using (XmlWriter XW = XmlWriter.Create("MZ.xml", Settings))
            {
                // 将数据写入XML
                SetConfigData.WriteObject(XW, SetConfing);
            }
        }
    }

    private void MainWindow_OnKeyDown(object sender, KeyEventArgs e)
    {
        if (Keyboard.IsKeyDown(Key.LeftCtrl) && Keyboard.IsKeyDown(Key.Up))
        {
            this.Top -= 1;
            SetConfing.WindowTop = Convert.ToInt32(this.Top);
        }
        else if (Keyboard.IsKeyDown(Key.LeftCtrl) && Keyboard.IsKeyDown(Key.Down))
        {
            this.Top += 1;
            SetConfing.WindowTop = Convert.ToInt32(this.Top);
        }
        else if (Keyboard.IsKeyDown(Key.LeftCtrl) && Keyboard.IsKeyDown(Key.Left))
        {
            this.Left -= 1;
            SetConfing.WindowLeft = Convert.ToInt32(this.Left);
        }
        else if (Keyboard.IsKeyDown(Key.LeftCtrl) && Keyboard.IsKeyDown(Key.Right))
        {
            this.Left += 1;
            SetConfing.WindowLeft = Convert.ToInt32(this.Left);
        }
        else if (Keyboard.IsKeyDown(Key.LeftCtrl) && Keyboard.IsKeyDown(Key.S))
        {
            // 创建序列化器
            DataContractSerializer SetConfigData = new DataContractSerializer(typeof(Config));
            // 声明XMLWriterSettings对象
            XmlWriterSettings Settings = new XmlWriterSettings { Indent = true };
            using (XmlWriter XW = XmlWriter.Create("MZ.xml", Settings))
            {
                // 将数据写入XML
                SetConfigData.WriteObject(XW, SetConfing);
            }
        }
    }
}